Skip to content

fix(tools): write_file preserves the existing file's line-ending style (CRLF) - #5911

Merged
Hmbown merged 1 commit into
Hmbown:mainfrom
EvanProgramming:fix/write-file-crlf
Sep 6, 2026
Merged

fix(tools): write_file preserves the existing file's line-ending style (CRLF)#5911
Hmbown merged 1 commit into
Hmbown:mainfrom
EvanProgramming:fix/write-file-crlf

Conversation

@EvanProgramming

Copy link
Copy Markdown
Contributor

Summary

Fixes #5909. write_file silently converted an existing CRLF (Windows) file to LF on overwrite, while edit_file preserves the detected line ending — so the same file behaved differently depending on the tool used. Both write paths were affected:

  • WriteFileTool::execute (full replace)
  • WriteFileTool::execute_contract_write (the hidden write_file / File contract path)

Root cause

Both paths wrote file_content.as_bytes() verbatim. Model-produced content is LF, so every \r\n in the on-disk file became \n — changing byte content the model never intended to touch, corrupting diffs/blame on CRLF-committed repos and on Windows.

Changes

  • New helper preserve_prior_line_endings(content, prior): normalizes incoming content to LF internally, then restores the prior file's detected ending (contract_line_ending), exactly mirroring edit_file's policy.
  • Both write paths call it. A brand-new file (no prior content) is written verbatim — there is no style to honor, so creation behavior is unchanged.

Tests

  • E2E: write_file over an existing CRLF file with LF content leaves the file CRLF.
  • Helper unit coverage: CRLF prior → CRLF out; LF prior → LF out; empty prior → verbatim (including CRLF input).

Verification

  • cargo check -p codewhale-tui --lib --tests passes.
  • cargo fmt -p codewhale-tui clean (only the touched files changed).

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Thanks @EvanProgramming for taking the time to contribute.

This repository is observing a maintainer-managed PR intake gate in dry-run mode, so this pull request is staying open. This note helps maintainers prepare the allowlist before any enforcement is considered.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant recurring PR access by commenting /lgtm on a pull request.

@Hmbown Hmbown left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified the bug on main — both write paths wrote content verbatim (crates/tui/src/tools/file.rs:1388, :1507) while edit_file preserved endings, so parity here is exactly right, and thanks for updating the diff/summary to use written so "(no changes)" stays truthful. Two small follow-ups: (1) normalize_contract_line_endings (file.rs:1568) also rewrites bare \r to the detected ending, so a lone CR in the new content becomes CRLF when the prior is CRLF — same policy as edit_file, but a unit test would pin it; (2) the contract path (execute_contract_write, file.rs:~1358) is only covered via the shared helper — one E2E test there would close the loop. Housekeeping: the commit is missing its Signed-off-by: trailer; mind amending with git commit --amend -s? Nice work, and thanks for the well-written #5909.

Overwriting an existing CRLF (Windows) file with write_file silently
rewrote every \r\n as \n, changing byte content the model did not intend
to touch and contradicting edit_file, which preserves the detected line
ending. Both write paths were affected:
WriteFileTool::execute and WriteFileTool::execute_contract_write.

Add preserve_prior_line_endings() (normalize to LF, then restore to the
prior file's detected ending) and use it in both paths. New files are
written verbatim — there is no prior style to honor.

Regression tests: an E2E write_file over a CRLF file stays CRLF, plus
direct helper coverage for CRLF/LF/empty-prior cases.

Closes Hmbown#5909.

Signed-off-by: EvanProgramming <evangonggyf@gmail.com>
@EvanProgramming

Copy link
Copy Markdown
Contributor Author

Thanks for the review — all three follow-ups are addressed in the updated commit (5693fbd4d):

  1. Lone-\r policy pinned: preserve_prior_line_endings_keeps_the_prior_style now asserts that a bare CR in the incoming content normalizes like edit_file (lone \r\n → re-emitted as the prior ending: CRLF prior → \r\n, LF prior → \n).
  2. Contract-path E2E added: contract_write_over_crlf_file_preserves_crlf_line_endings drives WriteFileTool::execute_contract_write over an existing CRLF file and asserts the on-disk result stays \r\n.
  3. Signed-off-by: trailer added via git commit --amend -s.

Verification: cargo check -p codewhale-tui --lib --tests passes and cargo fmt -p codewhale-tui is clean.

@Hmbown
Hmbown merged commit f10a468 into Hmbown:main Sep 6, 2026
36 of 37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

write_file silently converts CRLF files to LF on overwrite (edit_file preserves, write_file doesn't)

2 participants